package cn.zn.modules.pz.cur.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.zn.framework.common.utils.R;
import cn.zn.modules.budget.inter.BudgetFreezsInter;
import cn.zn.modules.budget.inter.vo.BudgetSGForFreezsVO;
import cn.zn.modules.loan.old.inter.LoanInter;
import cn.zn.modules.loan.old.inter.vo.LoanDetailVO;
import cn.zn.modules.pz.cur.entity.TpAttachment;
import cn.zn.modules.pz.cur.entity.TpMain;
import cn.zn.modules.pz.cur.entity.TpeFundingSource;
import cn.zn.modules.pz.cur.entity.dto.PzDetailDTO;
import cn.zn.modules.pz.cur.entity.dto.PzQueryDTO;
import cn.zn.modules.pz.cur.entity.dto.PzSaveAuthDTO;
import cn.zn.modules.pz.cur.service.TpAttachmentService;
import cn.zn.modules.pz.cur.service.TpMainService;
import cn.zn.modules.pz.cur.service.TpeFundingSourceService;
import cn.zn.modules.pz.enums.ExpenseWayEnum;
import cn.zn.modules.pz.enums.ProcessLevelEnum;
import cn.zn.modules.pz.enums.ProcessTypeEnum;
import cn.zn.modules.pz.enums.SpendingTypeEnum;
import cn.zn.modules.pz.his.entity.dto.PzExpenseDTO;
import cn.zn.modules.pz.his.entity.dto.PzRepayDTO;
import cn.zn.modules.pz.his.entity.dto.PzRequestDTO;
import cn.zn.modules.pz.inter.values.PzConf;
import cn.zn.modules.zzsys.inter.DeptInter;
import cn.zn.modules.zzsys.inter.UserRoleInter;
import cn.zn.modules.zzsys.inter.annotation.Login;
import cn.zn.modules.zzsys.inter.annotation.LoginUser;
import cn.zn.modules.zzsys.inter.dto.LoginUserDTO;
import cn.zn.modules.zzsys.inter.vo.DeptInfoVO;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Cleanup;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@RestController
@RequestMapping("pz/cur/pzmain")
@Slf4j
@Api(tags = "凭证")
public class TpMainController {

    @Autowired
    TpMainService tpMainService;
    @Autowired
    TpAttachmentService tpAttachmentService;
    @Autowired
    TpeFundingSourceService tpeFundingSourceService;
    @Autowired
    BudgetFreezsInter budgetFreezsInter;
    @Autowired
    private LoanInter loanInter;
    @Autowired
    private UserRoleInter userRoleInter;
    @Autowired
    private DeptInter deptInter;
    @Autowired
    private PzConf pzConf;
    // 凭证查询权限
    public static final String ROOT_PZ_QUERY = "109";
    public static final String ROOL_CWCZ = "cwcz";

    /**
     * 保存经费是否现金
     *
     * @param id
     * @param isCash
     */
    @PutMapping("saveMoneyType/{id}/{isCash}")
    public void saveMoneyType(@PathVariable String id, @PathVariable Boolean isCash) {
        tpMainService.saveMoneyType(id, isCash);
    }

    /**
     * 批量保存双高
     *
     * @param budgetSGForInfoVOS
     * @return
     */
    @PostMapping("/saveSGBatch")
    public R saveSGBatch(@RequestBody List<BudgetSGForFreezsVO> budgetSGForInfoVOS) {
        if (budgetFreezsInter != null) {
            budgetFreezsInter.saveSGBatch(budgetSGForInfoVOS);
        }
        return R.ok();
    }

    /**
     * 是否可以查看所有
     *
     * @param loginUserDTO
     * @return
     */
    @GetMapping("/canViewAll")
    @Login
    public R canViewAll(@LoginUser LoginUserDTO loginUserDTO) {
        boolean pzViewAll = false;
        boolean sgSetting = false;
        if (loginUserDTO != null) {
            pzViewAll = userRoleInter.hasRole(loginUserDTO.getSysId(), ROOT_PZ_QUERY);
            sgSetting = userRoleInter.hasRole(loginUserDTO.getSysId(), ROOL_CWCZ);
        }
        return R.ok()
                .put("pzViewAll", pzViewAll)
                .put("sgSetting", sgSetting)
                .put("initYear", pzConf.getInit().getYear());
    }

    /**
     * 列表
     *
     * @param dto
     * @return
     */
    @PostMapping("/list/{applyType}")
    @Login
    public R list(@LoginUser LoginUserDTO loginUser, @PathVariable("applyType") String applyType, @Valid @RequestBody PzQueryDTO dto) {
        if (dto.getOnlyMe()) {
            dto.setApplyUserCode(loginUser.getWorkNo());
        }
        dto.setApplyType(applyType);
        return R.ok().put("page", tpMainService.queryPage(dto));
    }

    /**
     * 详情
     *
     * @param pzQueryDTO
     * @return
     */
    @PostMapping("/detail")
    public R detail(@Valid @RequestBody PzDetailDTO pzQueryDTO) {
        return R.ok().put("pz", tpMainService.findDetail(pzQueryDTO));
    }

    /**
     * 新增单据
     *
     * @param applyType
     * @return
     */
    @GetMapping("/newApply/{applyType}")
    @Login
    public R newApply(@LoginUser LoginUserDTO loginUser, @PathVariable("applyType") String applyType) {
        return R.ok().put("pz", tpMainService.newApply(applyType, loginUser.getWorkNo()));
    }

    /**
     * 申请单中各选项数据
     *
     * @return
     */
    @GetMapping("/newApply/applySelectData")
    @Login
    public R applySelectData(@LoginUser LoginUserDTO loginUser) {
        List<DeptInfoVO> deptInfoVOS = deptInter.queryByLoginUser(loginUser.getSysId());
        return R.ok().put("depts", deptInfoVOS)
                .put("levels", ProcessLevelEnum.buildCombox())
                .put("spendingTypes", SpendingTypeEnum.buildCombox(
                        pzConf.getInit().getSpendingShowZj(),
                        pzConf.getInit().getSpendingShowZx(),
                        false))
                .put("expenseWays", ExpenseWayEnum.buildCombox());
    }

    /**
     * 保存请示
     *
     * @param pzRequestDTO
     * @return
     */
    @PostMapping("/saveApply/spending")
    public R saveApplySpending(@RequestBody PzRequestDTO pzRequestDTO) {
        tpMainService.saveApply(pzRequestDTO);
        return R.ok();
    }

    /**
     * 保存报销
     *
     * @param pzExpenseDTO
     * @return
     */
    @PostMapping("/saveApply/expense")
    public R saveApplyExpense(@RequestBody PzExpenseDTO pzExpenseDTO) {
        tpMainService.saveApply(pzExpenseDTO);
        return R.ok();
    }

    /**
     * 保存还款
     *
     * @param pzRepayDTO
     * @return
     */
    @PostMapping("/saveApply/repay")
    public R saveApplyRepay(@RequestBody PzRepayDTO pzRepayDTO) {
        tpMainService.saveApply(pzRepayDTO);
        return R.ok();
    }

    /**
     * 保存委托
     *
     * @return
     */
    @PutMapping("/saveAuth")
    public R saveAuth(@RequestBody PzSaveAuthDTO pzSaveAuth) {
        tpMainService.saveAuth(pzSaveAuth.getMainId(), pzSaveAuth.getAuthToUserCode());
        return R.ok();
    }

    /**
     * 生成普通报销单
     *
     * @param mainId
     * @return
     */
    @PostMapping("saveForExpense")
    public R saveForExpense(@RequestBody String[] mainId) {
        String mainIdExpense = tpMainService.saveForExpense(mainId[0], null);
        return R.ok().put("mainId", mainIdExpense);
    }

    /**
     * 生成补登报销单
     *
     * @param mainId
     * @return
     */
    @PostMapping("saveForExpenseAddendum")
    public R saveForExpenseAddendum(@RequestBody String[] mainId) {
        String mainIdExpense = tpMainService.saveForExpense(mainId[0], SpendingTypeEnum.type_4);
        return R.ok().put("mainId", mainIdExpense);
    }

    /**
     * 合并报销
     *
     * @param mainId
     * @return
     */
    @PostMapping("saveForExpenseMerge")
    @Login
    public R saveForExpenseMerge(@LoginUser LoginUserDTO loginUser, @RequestBody String[] mainId) {
        String mainIdExpense = tpMainService.saveForExpenseMerge(mainId, loginUser.getWorkNo(), "");
        return R.ok().put("mainId", mainIdExpense);
    }

    /**
     * 列表
     *
     * @return
     */
    @GetMapping("/listForRepay")
    @Login
    public R listForRepay(@LoginUser LoginUserDTO loginUser) {
        if (loanInter == null) {
            return R.error("未加载借款模块");
        }
        List<LoanDetailVO> loanDetails = loanInter.findByUserId(loginUser.getSysId(), true);
        if (loanDetails.size() == 0) {
            // 没有借款信息
            return R.ok().put("list", CollUtil.newArrayList());
        }
        PzQueryDTO dto = new PzQueryDTO();
        dto.setApplyType(ProcessTypeEnum.EXPENSE.getDeployKey());
        dto.setApplyUserCode(loginUser.getWorkNo());
        dto.setApplyCodes(loanDetails.stream().map(o -> o.getApplyCode()).collect(Collectors.toList()));
        return R.ok().put("list", tpMainService.queryPage(dto));
    }

    /**
     * 列表
     */
    @RequestMapping("exportBankZip")
    @ApiOperation("下载银行信息")
    public void exportBankZip(String appointMainIds, MultipartFile[] files, HttpServletResponse response) throws IOException {
        List<ExportBankInfo> exportBankInfos = Lists.newArrayList();
        List<ImgFile> imgFiles = null;
        List<BankInfo> bankInfos = null;
        List<TpAttachment> tpAttachments = null;
        List<TpeFundingSource> tpeFundingSources = null;
        String[] appointMainIdArr = appointMainIds.split(",");
        TpMain tpMain = null;
        for (String appointMainId : appointMainIdArr) {
            tpMain = tpMainService.findByAppontMainId(appointMainId);
            // 得到附件
            imgFiles = Lists.newArrayList();
            tpAttachments = tpAttachmentService.findEntityByMainId(tpMain.getId());
            for (TpAttachment tpAttachment : tpAttachments) {
                imgFiles.add(new ImgFile(tpAttachment.getRealName().concat(".").concat(tpAttachment.getSuffix()), tpAttachment.getPathLocal()));
            }
            // 得到银行信息
            tpeFundingSources = tpeFundingSourceService.findByMainId(tpMain.getId());
            bankInfos = Lists.newArrayList();
            for (TpeFundingSource fs : tpeFundingSources) {
                bankInfos.add(new BankInfo(fs.getReceivablesName(), fs.getReceivablesAccount(), fs.getReceivablesBank(), fs.getMoney(), fs.getRemark()));
            }
            // 整理一个报销单的打包信息
            exportBankInfos.add(new ExportBankInfo(bankInfos, imgFiles, tpMain.getApplyCode()));
        }

        // 生成文件，及打包
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        StringBuilder bankInfoStr = null;
        File file = null;
        int index = 0;
        for (ExportBankInfo exportBankInfo : exportBankInfos) {
            bankInfoStr = new StringBuilder();
            bankInfoStr.append("银行账户,");
            bankInfoStr.append("银行账号,");
            bankInfoStr.append("开户行,");
            bankInfoStr.append("说明,");
            bankInfoStr.append("金额,\r\n");
            for (BankInfo bankInfo : exportBankInfo.getBankInfos()) {
                bankInfoStr.append(this.formatBankNum(bankInfo.getReceivablesAccount())).append(",");
                bankInfoStr.append(bankInfo.getReceivablesName()).append(",");
                bankInfoStr.append(bankInfo.getReceivablesBank()).append(",");
                bankInfoStr.append(bankInfo.getRemark()).append(",");
                bankInfoStr.append(bankInfo.getMoney()).append(",\r\n");
            }
            zip.putNextEntry(new ZipEntry(exportBankInfo.getPackageName().concat(File.separator).concat("bank.csv")));
            IOUtils.write(bankInfoStr, zip, "UTF-8");
            zip.closeEntry();
            for (ImgFile imgFile : exportBankInfo.getImgFiles()) {
                file = new File(imgFile.getFilePath());
                if (file.exists()) {
                    zip.putNextEntry(new ZipEntry(exportBankInfo.getPackageName().concat(File.separator).concat(imgFile.getFileName())));
                    IOUtils.write(this.getImageStream(file), zip);
                    zip.closeEntry();
                }
            }
            // 添加凭证
            if (files != null && files.length > index) {
                zip.putNextEntry(new ZipEntry(exportBankInfo.getPackageName().concat(File.separator).concat(files[index].getOriginalFilename())));
                IOUtils.write(this.getImageStream(files[index].getInputStream()), zip);
                zip.closeEntry();
            }
            index++;
        }

        byte[] data = outputStream.toByteArray();

        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"bank.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");

        IOUtils.closeQuietly(zip);
//        IOUtils.write(data, response.getOutputStream());
        @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(new File("D:\\360安全浏览器下载\\aa.zip"));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        IOUtils.write(data, bufferedOutputStream);
    }

    /**
     * 图片转byte
     *
     * @param fis
     * @return
     */
    private byte[] getImageStream(InputStream fis) {
        byte[] buffer = null;
        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }

            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            log.error("", e);
        } catch (IOException e) {
            log.error("", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                log.error("", e);
            }
        }
        return buffer;
    }

    /**
     * 图片转byte
     *
     * @param imgFile
     * @return
     */
    private byte[] getImageStream(File imgFile) {
        byte[] buffer = null;
        ByteArrayOutputStream bos = null;
        FileInputStream fis = null;
        try {
            bos = new ByteArrayOutputStream();
            fis = new FileInputStream(imgFile);
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }

            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            log.error("", e);
        } catch (IOException e) {
            log.error("", e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                log.error("", e);
            }
        }
        return buffer;
    }

    /**
     * 格式化银行账号
     *
     * @param bankNum
     * @return
     */
    private String formatBankNum(String bankNum) {
        if (StrUtil.isBlank(bankNum)) {
            return "";
        }
        bankNum = bankNum.replace(" ", "");
        StringBuilder sb = new StringBuilder(bankNum);
        int length = bankNum.length() / 4 + bankNum.length();

        for (int i = 0; i < length; i++) {
            if (i % 5 == 0) {
                sb.insert(i, " ");
            }
        }
        sb.deleteCharAt(0);
        return sb.toString();
    }

    @Data
    @AllArgsConstructor
    class ExportBankInfo {
        List<BankInfo> bankInfos;
        List<ImgFile> imgFiles;
        String packageName;
    }

    @Data
    @AllArgsConstructor
    class BankInfo {
        /**
         * 收款名称
         */
        private String receivablesName;
        /**
         * 收款账号
         */
        private String receivablesAccount;
        /**
         * 开户行
         */
        private String receivablesBank;
        /**
         * 金额
         */
        private Double money;
        /**
         * 说明
         */
        private String remark;
    }

    @Data
    @AllArgsConstructor
    class ImgFile {
        String fileName;
        String filePath;
    }
}
